package com.feilx.changgou.search.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.changgou.pojo.SkuInfo;
import com.feilx.changgou.search.client.SkuClient;
import com.feilx.changgou.search.dao.EsDao;
import com.feilx.changgou.search.service.SearchService;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchPhaseResult;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.query.QuerySearchResult;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.ScoreSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.*;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.HighlightQueryBuilder;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import javax.jnlp.ClipboardService;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private EsDao esDao;


    //设置每页查询条数据
    public final static Integer PAGE_SIZE = 100;

    @Override
    public Map search(Map<String, String> paramMap) {
        Map<String,Object> resultMap = new HashMap<>();
            if(paramMap != null){
                // 构建组合条件查询
                BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
                if (StrUtil.isNotEmpty(paramMap.get("keywords"))){
                    boolQueryBuilder.must(QueryBuilders.matchQuery("name",paramMap.get("keywords")).operator(Operator.AND));
                }

                // 原生搜索实现类
                NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
                nativeSearchQueryBuilder.withQuery(boolQueryBuilder)
                        .withPageable(PageRequest.of(0,PAGE_SIZE))
                        //.withTrackScores(true)
                        .addAggregation(AggregationBuilders.terms("skuBrand").field("brandName").size(100000))
                        .addAggregation(AggregationBuilders.terms("skuSpec").field("spec.keyword").size(100000)); // 分组

                if (StrUtil.isNotEmpty(paramMap.get("keywords"))){
                    // 高亮搜索
                    HighlightBuilder.Field field = new HighlightBuilder.Field("name");
                    // 前缀
                    field.preTags("<em style='color:red;'>");
                    // 后缀
                    field.postTags("</em>");

                    nativeSearchQueryBuilder.withHighlightFields(field); // 高亮
                }

                    // 分类条件查询
                    // boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gt(100).lte(100));

                    // 排序
                    //ScoreSortBuilder ssb =new ScoreSortBuilder();
                    // nativeSearchQueryBuilder.withSort(ssb.order(SortOrder.valueOf()));


                SearchHits<SkuInfo> skuinfo = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), SkuInfo.class, IndexCoordinates.of("skuinfo"));

                List<SkuInfo> list = new ArrayList();
                skuinfo.stream().forEach( hit ->{
                    SkuInfo content = hit.getContent();
                  if(StrUtil.isNotEmpty(paramMap.get("keywords"))){
                      List<String> name = hit.getHighlightFields().get("name");
                      StringBuffer sb = new StringBuffer();  // 线程安全
                      name.stream().forEach(data-> {
                          sb.append(data);
                      });
                      content.setName(sb.toString());
                  }
                    list.add(content);
                });

                long skuinfo1 = elasticsearchRestTemplate.count(nativeSearchQueryBuilder.build(), IndexCoordinates.of("skuinfo"));
                // 分组聚合查询
                Aggregations aggregations = skuinfo.getAggregations();
                List<String> brands = getGroupData(aggregations, "skuBrand");
                List<String> specs = getGroupData(aggregations, "skuSpec");
                Map<String, Set<String>> specData = getSpecData(specs);

                long totalHits = skuinfo.getTotalHits();
                resultMap.put("rows",list);
                resultMap.put("brands",brands);
                resultMap.put("specs",specData);
                resultMap.put("total",totalHits);

            }

        return resultMap;
    }




    public Map getAll(Map<String, String> paramMap) throws IOException {
        // 构建指定文档搜索请求
        SearchRequest searchRequest = new SearchRequest("skuinfo");

        // 构建搜索资源 高亮 分页 排序  聚合分组 查询 过滤
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        searchSourceBuilder.aggregation(AggregationBuilders.terms("skuBrand").field("brandName").size(100000));
        // 默认情况下，terms aggregation将返回doc_count排序的前十个字词的存储桶。可以通过设置size参数来更改此默认行为 解决聚合分组数据不正确
        searchSourceBuilder.aggregation(AggregationBuilders.terms("skuSpec").field("spec.keyword").size(100000));

        searchSourceBuilder.trackTotalHits(true);
        // 分页
        searchSourceBuilder.from(1);
        searchSourceBuilder.size(1000);

        // 高亮显示
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("name"); // 高亮字段
        highlightBuilder.preTags("<em style='color:red;'>");
        highlightBuilder.postTags("</em>");
        highlightBuilder.requireFieldMatch(false); // 多个高亮显示



        // 构建多条件查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        if(StrUtil.isNotEmpty(paramMap.get("keywords"))){
           boolQueryBuilder.must(QueryBuilders.matchQuery("name", paramMap.get("keywords")).operator(Operator.AND));
           searchSourceBuilder.highlighter(highlightBuilder);
        }

       searchSourceBuilder.query(boolQueryBuilder);
        // 构建搜索资源
       searchRequest.source(searchSourceBuilder);

        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);


        Map map = new HashMap();
        org.elasticsearch.search.SearchHit[] hits = response.getHits().getHits();
        List content = new ArrayList();
        for (org.elasticsearch.search.SearchHit hit : hits) {
           //  Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            String skuStr = hit.getSourceAsString();

            SkuInfo skuInfo = JSON.parseObject(skuStr, SkuInfo.class);

            if(StrUtil.isNotEmpty(paramMap.get("keywords"))){
                HighlightField highlightField = hit.getHighlightFields().get("name");
                Text[] fragments = highlightField.getFragments();
                StringBuffer sb = new StringBuffer();
                for (Text fragment : fragments) {
                    sb.append(fragment);
                }
                skuInfo.setName(sb.toString());
            }
           // content.add(sourceAsMap);
            content.add(skuInfo);
        }

        Aggregations aggregations = response.getAggregations();
        List<String> brandList = getGroupData(aggregations, "skuBrand");
        List<String> skuSpec = getGroupData(aggregations, "skuSpec");
        Map<String, Set<String>> specData = getSpecData(skuSpec);
        TotalHits totalHits = response.getHits().getTotalHits();
        map.put("rows",content);
        map.put("brandList", brandList);
        map.put("total",totalHits.value);
        map.put("specList", specData);
        return  map;
    }

    /*
    *  获取聚合分组数据
    * */
    private List<String> getGroupData(Aggregations aggregations,String groupName){
        Map<String, Aggregation> asMap =  aggregations.getAsMap();
        ParsedStringTerms terms= (ParsedStringTerms)asMap.get(groupName);
        List list = new ArrayList();
        terms.getBuckets().stream().forEach( bucket-> {
            list.add(((Terms.Bucket) bucket).getKeyAsString());
        });
        return  list;
    }
    // 规格过滤
    private Map<String, Set<String>> getSpecData(List<String> specs){
        // 数据转换成map
        if(specs.size() <= 0 && specs == null){
            return null;
        }
        Map<String, Set<String>> specAll = new HashMap<>();
        for (String spec : specs) {
            Map<String,String> map = JSON.parseObject(spec, Map.class);
            for (Map.Entry<String, String> entry : map.entrySet()) {
                String key = entry.getKey();  // 规格名
                String value = entry.getValue(); // 规格值
                Set<String> set = specAll.get(key);
                if(set == null ){
                    set = new HashSet<>();
                }
                set.add(value);
                specAll.put(key,set);
            }
        }


        return  specAll;
    }
}
